/**
 * The Prophecy
 * java based lightweight texas hold'em poker software base
 * http://code.google.com/p/theprophecy/
 */

package org.poker.prophecy.player.primitiv;

import org.poker.prophecy.math.Chance;
import org.poker.prophecy.player.general.Player;
import org.poker.prophecy.player.preflopstyles.StartingHandsAnalysisSamBraids;
import org.poker.prophecy.simulation.Board;
import org.poker.prophecy.simulation.Table;
import org.poker.prophecy.simulation.action.AbstractAction;
import org.poker.prophecy.simulation.action.CallAction;
import org.poker.prophecy.simulation.action.CheckAction;
import org.poker.prophecy.simulation.action.FoldAction;
import org.poker.prophecy.simulation.action.RaiseAction;
import org.poker.prophecy.simulation.process.ProcessType;
import org.poker.prophecy.simulation.process.RoundProcess;

import com.javaflair.pokerprophesier.api.adapter.PokerProphesierAdapter;
import com.javaflair.pokerprophesier.api.card.Card;
import com.javaflair.pokerprophesier.api.card.CommunityCards;
import com.javaflair.pokerprophesier.api.card.HoleCards;
import com.javaflair.pokerprophesier.api.exception.SimulatorException;
import com.javaflair.pokerprophesier.api.helper.MyGameStatsHelper;
import com.javaflair.pokerprophesier.api.helper.MyHandHelper;
import com.javaflair.pokerprophesier.api.helper.MyHandStatsHelper;
import com.javaflair.pokerprophesier.api.helper.MyOutsHelper;
import com.javaflair.pokerprophesier.api.helper.OppHandStatsHelper;

/**
 * FirstDornPlayer
 * 
 * Random try-outs
 * 
 * @author tusek
 *
 */
public class FirstDornPlayer extends Player {
	
	Board board = null;
	
    // pp adapter to communicate with the Poker Prophesier simulator
    PokerProphesierAdapter ppAdapter = null;
    // pp hole cards
    HoleCards ppHoleCards = null; 
    // pp comunity cards
    CommunityCards ppCommunityCards = null;
    // pp Number of simulations, total players
    int ppNumSimulations = 100000;
    int ppNumSimPlayers = 8;    
    // pp Get the MyHandHelper
    MyHandHelper ppHandHelper = null;
    // pp Get the MyOutsHelper
    MyOutsHelper ppOutsHelper = null;
    // pp Get the MyHandStatsHelper
    MyHandStatsHelper ppHandStatsHelper = null;
    // pp Get the OppHandStatsHelper
    OppHandStatsHelper ppOppHandStatsHelper = null;
    // pp Get the MyGameStatsHelper
    MyGameStatsHelper ppGameStatsHelper = null;
    
	
	public FirstDornPlayer(String name, int money, Table table){
		this.setName(name);
		this.setMoney(money);
		this.setTable(table);
		
		// init adapter to communicate with the Poker Prophesier simulator
		ppAdapter = new PokerProphesierAdapter();
		ppNumSimPlayers = table.getPlayers().size();
        // Set the simulator parameters
		ppAdapter.setMyOutsHoleCardSensitive(true);
		ppAdapter.setOppHoleCardsRealistic(true);
		ppAdapter.setOppProbMyHandSensitive(true);
		
		ppAdapter.setNumSimulations(ppNumSimulations);

	}	

	public AbstractAction actPreFlop(RoundProcess roundProcess) {
		roundProcess.getTicker().tick(this.getName()+" says: My Pocket cards are "+this.getPocketCards()+", Sam Braids rates them "+StartingHandsAnalysisSamBraids.getStartingHandRatingAsPrintableString(this.getPocketCards())+".");
		roundProcess.getTicker().talk(this, StartingHandsAnalysisSamBraids.getStartingHandRatingAsPrintableString(this.getPocketCards()));
		
		//init ppHoleCards
		this.ppSetHoleCards();
		
		this.ppRunSimulator(roundProcess);
		float ppWinProb = ppGameStatsHelper.getWinProb();
		roundProcess.getTicker().talk(this, String.valueOf(ppWinProb));
		
		int maxnow = roundProcess.getPot().getMaximumAmountPaidByOnePersonInThisProcess(roundProcess.getCurrentProcessType());
		
		// if I'm out of money and I can't check, i'm folding
		if (!canICheck(roundProcess) && this.getMoney() <= 0) return new FoldAction(this, roundProcess);
		
		// check if the dealer fell a sleep
		if (!doIReallyHaveAnActionLeft(roundProcess)){ 
			//roundProcess.getTicker().talk(this, "CHECK");
			return new CheckAction(this, roundProcess);
		}
		
		// do some action
		if (canICheck(roundProcess)){
			if(ppWinProb > 0.5 && this.getMoney() > 0){
				//roundProcess.getTicker().talk(this, "RAISE");
				return new RaiseAction(this, roundProcess);
			}
				
			//roundProcess.getTicker().talk(this, "CHECK"); 
			return new CheckAction(this, roundProcess);
		}
		else{
			//if premium then raise
			if(StartingHandsAnalysisSamBraids.isPremiumStartingHand(this.getPocketCards()) && this.getMoney() > 0){
				//roundProcess.getTicker().talk(this, "RAISE");
				return new RaiseAction(this, roundProcess);
			}	
			
			//call if NOT garbage
			if(!StartingHandsAnalysisSamBraids.isGarbageStartingHand(this.getPocketCards())){
				//roundProcess.getTicker().talk(this, "CALL");
				return new CallAction(this, roundProcess);
			}
			
			//roundProcess.getTicker().talk(this, "FOLD");
			return new FoldAction(this, roundProcess);
		}
	}

	public AbstractAction actPostFlop(RoundProcess roundProcess) {
		return actTheSameInEveryProcessType(roundProcess);
	}

	public AbstractAction actPostTurn(RoundProcess roundProcess) {
		return actTheSameInEveryProcessType(roundProcess);
	}

	public AbstractAction actPostRiver(RoundProcess roundProcess) {
		return actTheSameInEveryProcessType(roundProcess);
	}

	
	private AbstractAction actTheSameInEveryProcessType(RoundProcess roundProcess) {
		//init ppComunityCards
		this.ppSetComunityCards(roundProcess);
		
		this.ppRunSimulator(roundProcess);
		float ppWinProb = ppGameStatsHelper.getWinProb();
		roundProcess.getTicker().talk(this, String.valueOf(ppWinProb));
		
		int maxnow = roundProcess.getPot().getMaximumAmountPaidByOnePersonInThisProcess(roundProcess.getCurrentProcessType());
		
		int myMoneyInPot = roundProcess.getPot().howMuchMoneyDoesThisPlayerHavePutInThePotInThisRoundYet(this);	
		int potSize = roundProcess.getPot().getPotSize();
		
		// if I'm out of money and I can't check, i'm folding
		if (!canICheck(roundProcess) && this.getMoney() <= 0) return new FoldAction(this, roundProcess);

		
		// check if the dealer fell a sleep
		if (!doIReallyHaveAnActionLeft(roundProcess)) 
			return new CheckAction(this, roundProcess);
		
		
		// some random actions
		if (canICheck(roundProcess)){
			if(ppWinProb > 0.5 && this.getMoney() > 0){
				//roundProcess.getTicker().talk(this, "RAISE");
				return new RaiseAction(this, roundProcess);
			 
			}
			//roundProcess.getTicker().talk(this, "CHECK");
			return new CheckAction(this, roundProcess);
		}
		else{
			// raise sometimes
			if(ppWinProb > 0.5 && this.getMoney() > 0){
				//roundProcess.getTicker().talk(this, "RAISE");
				return new RaiseAction(this, roundProcess);
			}
				
			
			// this does not make sense
			if((myMoneyInPot > this.getMoney()) || (potSize * 4 > this.getMoney())){
				//roundProcess.getTicker().talk(this, "CALL");
				return new CallAction(this, roundProcess);
			
			}

			// don't fold so easily
			if(Chance.randomYesOrNo()){
				//roundProcess.getTicker().talk(this, "CALL");
				return new CallAction(this, roundProcess);
			}
			
			// well ok
			//roundProcess.getTicker().talk(this, "FOLD");
			return new FoldAction(this, roundProcess);
		}	
	}
	
	
	private boolean canICheck(RoundProcess roundProcess){
		
		int maxnow = roundProcess.getPot().getMaximumAmountPaidByOnePersonInThisProcess(roundProcess.getCurrentProcessType());
		int maxMe = roundProcess.getPot().howMuchMoneyDoesThisPlayerHavePutInThePotInProcessYet(this, roundProcess.getCurrentProcessType());
		
		if(maxMe == maxnow) return true;
		if(maxMe > maxnow){
			roundProcess.getTicker().tick(this.getName()+" says: someone's cheating!");
			return true;
		}
		return false;
	}
	
	
	private boolean didGotCalled(RoundProcess roundProcess){
		int maxnow = roundProcess.getPot().getMaximumAmountPaidByOnePersonInThisProcess(roundProcess.getCurrentProcessType());
		int maxMe = roundProcess.getPot().howMuchMoneyDoesThisPlayerHavePutInThePotInProcessYet(this, roundProcess.getCurrentProcessType());		
		
		//if this is preflop and im the big blind and its even pot then this is the only exception
		if( roundProcess.getCurrentProcessType() == ProcessType.PREFLOP && 
		    roundProcess.getBigBlindPosterThisRound().equals(this) && 
		    maxnow == maxMe ) return false; 
		
		//if i put money in this round and its even pot now then i got called
		if( maxMe > 0 && maxMe == maxnow) return true;
		
		return false;
	}

	
	private boolean doIReallyHaveAnActionLeft(RoundProcess roundProcess){
		
		//if I can't check, then it must be so
		if(!this.canICheck(roundProcess)) return true;
		
		//if I got called, then not
		if(didGotCalled(roundProcess)){
			roundProcess.getTicker().tick(this.getName()+" says: wasn't the round over? hmm.. i'll check");
			return false;
		}
		
		return true;
	}	
	
	
	
	private void ppSetHoleCards(){
		char hole1Value = this.getPocketCards().getCard1().getValue();
		char hole1Suit = this.getPocketCards().getCard1().getSuit();
		char hole2Value = this.getPocketCards().getCard2().getValue();
		char hole2Suit = this.getPocketCards().getCard2().getSuit();
		
		this.transToPPCard(hole1Value, hole1Suit);
		
		ppHoleCards = new HoleCards(
				this.transToPPCard(hole1Value, hole1Suit), 
				this.transToPPCard(hole2Value, hole2Suit)
		);			
	}
	
	
	private void ppSetComunityCards(RoundProcess roundProcess){
		board = this.getTable().getBoard();
		
		if( roundProcess.getCurrentProcessType() == ProcessType.POSTFLOP){
			ppCommunityCards = new CommunityCards(new Card[]{
			   this.transToPPCard(board.getFlop1().getValue(), board.getFlop1().getSuit()),
			   this.transToPPCard(board.getFlop2().getValue(), board.getFlop2().getSuit()),
			   this.transToPPCard(board.getFlop3().getValue(), board.getFlop3().getSuit())
			});
		}

		if( roundProcess.getCurrentProcessType() == ProcessType.POSTTURN){
			ppCommunityCards = new CommunityCards(new Card[]{
			   this.transToPPCard(board.getFlop1().getValue(), board.getFlop1().getSuit()),
			   this.transToPPCard(board.getFlop2().getValue(), board.getFlop2().getSuit()),
			   this.transToPPCard(board.getFlop3().getValue(), board.getFlop3().getSuit()),
			   this.transToPPCard(board.getTurn().getValue(), board.getTurn().getSuit())
			});
		}

		if( roundProcess.getCurrentProcessType() == ProcessType.POSTRIVER){
			ppCommunityCards = new CommunityCards(new Card[]{
			   this.transToPPCard(board.getFlop1().getValue(), board.getFlop1().getSuit()),
			   this.transToPPCard(board.getFlop2().getValue(), board.getFlop2().getSuit()),
			   this.transToPPCard(board.getFlop3().getValue(), board.getFlop3().getSuit()),
			   this.transToPPCard(board.getTurn().getValue(), board.getTurn().getSuit()),
			   this.transToPPCard(board.getRiver().getValue(), board.getRiver().getSuit())
			});
		}
	}
	
	
	private Card transToPPCard(char value, char suit){
		
		int ppVal = 0;
		int ppSui = 0;
		
        switch (value){
        case '2': ppVal = Card.TWO; break;
        case '3': ppVal = Card.THREE; break;
        case '4': ppVal = Card.FOUR; break;
        case '5': ppVal = Card.FIVE; break;
        case '6': ppVal = Card.SIX; break;
        case '7': ppVal = Card.SEVEN; break;
        case '8': ppVal = Card.EIGHT; break;
        case '9': ppVal = Card.NINE; break;
        case 't': case 'T': ppVal = Card.TEN; break;
        case 'j': case 'J': ppVal = Card.JACK; break;
        case 'q': case 'Q': ppVal = Card.QUEEN; break;
        case 'k': case 'K': ppVal = Card.KING; break;
        case 'a': case 'A': ppVal = Card.ACE; break;
        default:             
            //TODO
            System.out.println("Die Eingabe stinkt");break;
        }
    
        switch (suit){
        case 'h': case 'H': ppSui = Card.HEARTS; break;
        case 'd': case 'D': ppSui = Card.DIAMONDS; break;
        case 's': case 'S': ppSui = Card.SPADES; break;
        case 'c': case 'C': ppSui = Card.CLUBS; break;
        default:             
            //TODO
            System.out.println("Die Eingabe stinkt");break;
        }
		
        return new Card(ppVal, ppSui);
	}
	
	
	
	private void ppRunSimulator(RoundProcess roundProcess){
		
		//update players count
		ppNumSimPlayers = roundProcess.getCurrentPlayersWhoHaventFolded().size();
		
		int ppState = PokerProphesierAdapter.NO_CARDS_DEALT;
		
		if(roundProcess != null && roundProcess.getCurrentProcessType() == ProcessType.PREFLOP){
			ppState = PokerProphesierAdapter.HOLE_CARDS_DEALT;
			ppCommunityCards = null;
		}
		
		if(roundProcess != null && roundProcess.getCurrentProcessType() == ProcessType.POSTFLOP)
			ppState = PokerProphesierAdapter.FLOP_CARDS_DEALT;

		if(roundProcess != null && roundProcess.getCurrentProcessType() == ProcessType.POSTTURN)
			ppState = PokerProphesierAdapter.TURN_CARD_DEALT;

		if(roundProcess != null && roundProcess.getCurrentProcessType() == ProcessType.POSTRIVER)
			ppState = PokerProphesierAdapter.RIVER_CARD_DEALT;
		
		
        // Run the simulator
        try{
            ppAdapter.runMySimulations(ppHoleCards, ppCommunityCards, ppNumSimPlayers, ppState);
        }
        catch (SimulatorException e){
            e.printStackTrace();
        }		
        
        
        // Get the MyHandHelper
        //ppHandHelper = ppAdapter.getMyHandHelper();
        //roundProcess.getTicker().tick(this.getName()+" my hand:\n" + ppHandHelper);

        // Get the MyOutsHelper
        //ppOutsHelper= ppAdapter.getMyOutsHelper();
        //roundProcess.getTicker().tick(this.getName()+" my outs:\n" + ppOutsHelper);
        

        // Get the MyHandStatsHelper
        //ppHandStatsHelper= ppAdapter.getMyHandStatsHelper();
        //roundProcess.getTicker().tick(this.getName()+" my hand stats:\n" + ppHandStatsHelper);
        

        // Get the OppHandStatsHelper
        //ppOppHandStatsHelper= ppAdapter.getOppHandStatsHelper();
        //roundProcess.getTicker().tick(this.getName()+" opponent hands stats:\n" + ppOppHandStatsHelper);
        

        // Get the MyGameStatsHelper
        ppGameStatsHelper= ppAdapter.getMyGameStatsHelper();
        roundProcess.getTicker().tick(this.getName()+" my game stats:" + ppGameStatsHelper);
                
        
	}
	
	
}
